/*
 * Copyright (C) 2012 mAPPn.Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mol.market.common.widget;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Checkable;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RatingBar;
import android.widget.TextView;

import com.mol.market.R;
import com.mol.market.common.util.ImageUtils;
import com.mol.market.demo.widget.LazyLoadAbsListViewFragment;

/**
 * 延迟加载适配器。
 *
 * Author: Andrew.Wang
 * Date  : 12-11-13
 */
public class LazyLoadListAdapter extends BaseAdapter implements Observer {

    private final Context mContext;
    private final String[] mFrom;
    private final int[] mTo;
    private final int mResource;
    private final ArrayList<HashMap<String, Object>> mDataSource;
    private final LayoutInflater mInflater;

    private final LazyLoadAbsListViewFragment mLazyLoadListFragment;

    private ViewBinder mViewBinder;

    /**
     *
     * @param data
     * @param resource
     * @param from
     * @param to
     */
    public LazyLoadListAdapter(LazyLoadAbsListViewFragment fragment,
        ArrayList<HashMap<String, Object>> data, int resource, String[] from, int[] to) {

        mContext = fragment.getActivity();
        mLazyLoadListFragment = fragment;
        mResource = resource;

        if (data == null) {
            mDataSource = new ArrayList<HashMap<String, Object>>();
        } else {
            mDataSource = data;
        }
        mFrom = from;
        mTo = to;
        mInflater = LayoutInflater.from(mContext);
    }

    public void insertData(HashMap<String, Object> newData) {
        if (newData != null) {
            mDataSource.add(0, newData);
            notifyDataSetChanged();
        }
    }

    public void addData(HashMap<String, Object> newData) {
        if (newData != null) {
            mDataSource.add(getCount(), newData);
            notifyDataSetChanged();
        }
    }

    public void addData(ArrayList<HashMap<String, Object>> newData) {
        mDataSource.addAll(newData);
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        if (mDataSource == null) {
            return 0;
        }
        return mDataSource.size();
    }


    @Override
    public Object getItem(int position) {
        if (mDataSource != null && position < getCount()) {
            return mDataSource.get(position);
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public boolean isEmpty() {
        if (mDataSource == null || mDataSource.size() == 0) {
            return true;
        }
        return super.isEmpty();
    }


    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        // 当滑动到列表底部时触发异步加载任务
        if (position == getCount() - 4) {
            mLazyLoadListFragment.doLazyLoad();
        }

        View v;
        if (convertView == null) {
            v = newView(position, parent);
        } else {
            v = convertView;
        }

        bindView(position, v);
        return v;
    }

    /**
     * Create new view object and cache some views associated with it
     */

    private View newView(int position, ViewGroup parent) {
        View v = null;
//        if (mHasGroup && isPlaceHolder(position)) {
//            v = mInflater.inflate(mDividerResource, parent, false);
//        } else {
            v = mInflater.inflate(mResource, parent, false);
            ((ViewGroup) v).setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
//        }

        final int[] to = mTo;
        final int count = to.length;
        final View[] holder = new View[count];

        for (int i = 0; i < count; i++) {
            holder[i] = v.findViewById(to[i]);
        }
        v.setTag(holder);
        return v;
    }

    /**
     * bind the background data to the view
     */
    private void bindView(int position, View view) {

        final HashMap<String, Object> dataSet = mDataSource.get(position);
        if (dataSet == null) {
            return;
        }

        final View[] holder = (View[]) view.getTag();
        final int count = mTo.length;

        for (int i = 0; i < count; i++) {

            final View v = holder[i];

            if (v != null) {

                final Object data = dataSet.get(mFrom[i]);
                if (data == null) {
                    // make this view invisible if value is empty
                    v.setVisibility(View.GONE);
                    continue;
                }
                v.setVisibility(View.VISIBLE);

                //injection
                setInjection(v);

                boolean bound = false;
                if (mViewBinder != null) {
                    bound = mViewBinder.setViewValue(v, dataSet, position, data);
                }
                if (bound) {
                    continue;
                }

                if (v instanceof Checkable) {

                    v.setTag(position);
                    if (data instanceof Boolean) {
                        ((Checkable) v).setChecked((Boolean) data);
                    } else {
                        throw new IllegalStateException(v.getClass().getName()
                            + " should be bound to a Boolean, not a " + data.getClass());
                    }
                } else if (v instanceof Button) {

                    v.setTag(data);

                } else if (v instanceof ImageButton) {

                    // Note: keep the instanceof ImageButton be Top of ImageView
                    // since ImageButton is ImageView
                    // TODO setButtonImage((ImageButton) v, text);
                } else if (v instanceof ImageView) {

                    setViewImage(position, (ImageView) v, data);

                } else if (v instanceof RatingBar) {

                    setViewRating((RatingBar) v, data);

                } else if (v instanceof TextView) {

                    // Note: keep the instanceof TextView check at the bottom of
                    // these if since a lot of views are TextViews (e.g.
                    // CheckBoxes).
                    setViewText(position, (TextView) v, data);

                } else {
                    throw new IllegalStateException(v.getClass().getName() + " is not a "
                        + " view that can be bounds by this SimpleAdapter");
                }
            }
        }
    }

    /**
     * 填充ImageView内容。
     */
    private void setViewImage(int position, ImageView v, Object data) {

        // clear the CALLBACK reference to prevent of OOM error
        Drawable trash = v.getDrawable();
        if (trash != null) {
            trash.setCallback(null);
        }

        if (data instanceof Integer) {
            v.setImageResource((Integer) data);
        } else if (data instanceof Drawable) {
            v.setImageDrawable((Drawable) data);
        } else if (data instanceof Bitmap) {
            v.setImageBitmap((Bitmap) data);
        } else if (data instanceof Boolean){
            if(!(Boolean) data){
                v.setVisibility(View.GONE);

                //FIXME 必须为flagView留出空间
                if(v.getTag() != null){
                    v.setVisibility(View.INVISIBLE);
                }
            }
        }else if (data instanceof String) {
            // here is one remote object (URL)
            if (v.getId() == R.id.iv_user_icon) {
                ImageUtils.download(mContext, (String) data, v,
                    R.drawable.default_user_big, false, false);
            } else {
                ImageUtils.download(mContext, (String) data, v, false);
            }
            
        }
    }

    /**
     * 填充RatingBar内容。
     */
    private void setViewRating(RatingBar v, Object rating) {
        if (rating instanceof Float) {
            v.setRating((Float) rating);
        }
    }

    /**
     * 填充TextView内容。
     */
    private void setViewText(int position, TextView v, Object data) {

        if (data instanceof CharSequence) {
            v.setText((CharSequence) data);
        }
    }

    @Override
    public void update(Observable observable, Object data) {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    /**
     * Sets the binder used to bind data to views.
     *
     * @param viewBinder the binder used to bind data to views, can be null to
     *        remove the existing binder
     */
    public void setViewBinder(ViewBinder viewBinder) {
        mViewBinder = viewBinder;
    }

    /**
     * This class can be used by external clients of SimpleAdapter to bind
     * values to views.
     *
     * You should use this class to bind values to views that are not
     * directly supported by SimpleAdapter or to change the way binding
     * occurs for views supported by SimpleAdapter.
     *
     * @see android.widget.SimpleAdapter#setViewImage(android.widget.ImageView, int)
     * @see android.widget.SimpleAdapter#setViewImage(android.widget.ImageView, String)
     * @see android.widget.SimpleAdapter#setViewText(android.widget.TextView, String)
     */
    public static interface ViewBinder {
        /**
         * Binds the specified data to the specified view.
         *
         * When binding is handled by this ViewBinder, this method must return true.
         * If this method returns false, SimpleAdapter will attempts to handle
         * the binding on its own.
         *
         * @param view the view to bind the data to
         * @param data the data to bind to the view
         * @param position the item position in the list
         * @param textRepresentation a safe String representation of the supplied data:
         *        it is either the result of data.toString() or an empty String but it
         *        is never null
         *
         * @return true if the data was bound to the view, false otherwise
         */
        boolean setViewValue(View view, Object data, int position, Object textRepresentation);
    }

    //********************* injection ********************
    private SparseArray<View.OnClickListener> mInjectionPoller = new SparseArray<View.OnClickListener>();

    public void injection(int id, View.OnClickListener what,boolean remove) {
        if(remove){
            mInjectionPoller.remove(id);
        }else{
            mInjectionPoller.put(id, what);
        }
    }

    private void setInjection(View v){

        int id = v.getId();

        if(id == 0){
            //默认Id，不需要注入
            return ;
        }
        if(mInjectionPoller.get(id) != null){
//            v.setOnClickListener(this);
        }
    }

    public void clear(){
        mDataSource.clear();
        notifyDataSetChanged();
    }
    
    //********************* injection end ********************

    public void onClick(View v) {
        View.OnClickListener injectionListener = mInjectionPoller.get(v.getId());
        boolean isInjection = (injectionListener != null);

        if(isInjection){
            injectionListener.onClick(v);
        }
    }
}
